package servlet;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dao.InformMessageDao;
import dao.IntegralDao;
import dao.UserDao;
import dao.VipDao;
import dao.impl.InformMessageDaoImpl;
import dao.impl.IntegralDaoImpl;
import dao.impl.UserDaoImpl;
import dao.impl.VipDaoImpl;
import domian.*;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import redis.clients.jedis.Jedis;
import service.DownInfoService;
import service.FileClassService;
import service.FileService;
import service.UserService;
import service.impl.DownInfoServiceImpl;
import service.impl.FileClassServiceImpl;
import service.impl.FileServiceImpl;
import service.impl.UserServiceImpl;
import utils.Redis;
import utils.ToolSet;
import utils.ToolUtil;
import utils.UploadImageUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;


@WebServlet("/download/*")
@MultipartConfig
@SuppressWarnings("all")//抑制警告
public class DownLoadServlet extends BaseServlet {
    FileClassService fileClassService = new FileClassServiceImpl();
    FileService fileService = new FileServiceImpl();
    DownInfoService downInfoService = new DownInfoServiceImpl();
    UserDao userDao = new UserDaoImpl();
    IntegralDao integralDao = new IntegralDaoImpl();
    VipDao vipDao = new VipDaoImpl();
    InformMessageDao informMessageDao = new InformMessageDaoImpl();
    UserService us = new UserServiceImpl();

    /**
     * 初始化类别
     *
     * @param req
     * @param resp
     */
    public void init(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        System.out.println(req.getParameter("resourceName"));
        List<FileClassName> classByCid = fileClassService.findTitle();
        try {
            ToolSet.printWriter(resp, classByCid);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        System.out.println("集合");
        System.out.println(classByCid);
    }

    /**
     * 资源上传
     */
    public void upload(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, FileUploadException {
        //创建磁盘文件工厂
        DiskFileItemFactory factory = new DiskFileItemFactory();
        //创建文件上传类的对象
        ServletFileUpload upload = new ServletFileUpload(factory);
        //设置文件上传的编码
        upload.setHeaderEncoding("utf-8");
        //获取多部文件上传表单的集合，集合中存储的是每一个表单项
        List<FileItem> list = upload.parseRequest(req);
        //判断验证码是否正确
        String checkcodeServer = (String) req.getSession().getAttribute("CHECKCODE_SERVER");
        String code = req.getParameter("code");
        System.out.println("生成的:" + checkcodeServer);
        //判断上传的表单是不是带文件的，如果不是终止进行
        if (!ServletFileUpload.isMultipartContent(req)) {
            return;
        }
        ObjectMapper objectMapper = new ObjectMapper();
        //创建一个文件对象
        domian.File file = new domian.File();
        //获取Redis对象
        Jedis jedis = new Jedis();
        //获取用户客户端标识
        String redisKey = Redis.getRedisKey(req);
        String user = jedis.get(redisKey);
        //转化为java对象
        User user1 = objectMapper.readValue(user, User.class);
        //获取当前用户姓名
        //设置文件上传作者
        file.setF_userId(user1.getUid());
        System.out.println("表单项:" + list);
        //记录验证码正否
        if (list != null) {
            for (FileItem item : list) {
                //判断是普通表单项还是文件上传表单项
                if (item.isFormField()) {
                    //普通表单项
                    String fieldName = item.getFieldName();
                    String value = item.getString("utf-8");
                    if ("code".equals(fieldName)) {
                        //判断验证码是否正确
                        if (!checkcodeServer.equalsIgnoreCase(value)) {
                            System.out.println("验证码错误");
                            ToolSet.printWriter(resp, new Result(ResultStatus.UPLOAD_CODE_NO.getCode(), ResultStatus.UPLOAD_CODE_NO.getMsg()));
                            return;
                        }
                    }
                    if ("resourceName".equals(fieldName)) {
                        //资源名称
                        file.setF_title(value);
                    }
                    if ("describe".equals(fieldName)) {
                        //资源描述
                        file.setF_describe(value);
                        System.out.println("资源描述:" + value);
                    }
                    if ("score".equals(fieldName)) {
                        //资源分类 以id形式
                        file.setF_integral(Integer.parseInt(value));
                    }
                    if ("tags".equals(fieldName)) {
                        //文件标签
                        file.setF_lable(value);
                    }
                    if ("fid".equals(fieldName)) {
                        //文件分类id
                        file.setF_classId(Integer.parseInt(value));
                    }
                }
            }
        }
        for (FileItem item : list) {
            if (!item.isFormField()) {
                //仅当验证码正确
                //文件表单项
                String fileName = item.getName();
                //获取文件大小
                long size = item.getSize();
                System.out.println("文件大小:" + size);
                double fileSizeInMB = (double) size / (1024 * 1024);
                //创建一个DecimalFormat对象,指定小数点后两位
                DecimalFormat df = new DecimalFormat("#.00");
                //使用DecimalFormat格式化double数，并四舍五入保留两位小数
                String formattedNumber = df.format(fileSizeInMB);
                //如果文件过于小 按照0.01MB算
                if ("0.00".equals(formattedNumber)) {
                    formattedNumber = "0.01";
                }
                //讲字符串的double数据转化为int
                if (Math.round(Double.valueOf(formattedNumber)) >= 1000) {
                    System.out.println("文件大小超出限制");
                    ToolSet.printWriter(resp, new Result(ResultStatus.UPLOAD_SIZE_NO.getCode(), ResultStatus.UPLOAD_SIZE_NO.getMsg()));
                    return;
                }
                //设置文件大小
                file.setF_size(Double.parseDouble(formattedNumber));
                //避免重名 用时间戳辨别
                fileName = System.currentTimeMillis() + "" + fileName;
                //设置文件名称
                file.setF_name(fileName);
                //获取文件输入流对象
                InputStream is = item.getInputStream();
                // 指定上传文件的保存路径
                String uploadPath = this.getServletContext().getRealPath("files");
                File uploadFile = new File(uploadPath);
                if (!uploadFile.exists()) {
                    uploadFile.mkdir();//创建这个目录
                }
//                System.out.println(uploadPath);
                /**
                 * 此路径上传为相对于tomcat的相对路径
                 * 但由于不能实时更新先用绝对路径替代
                 */
                //FileOutputStream fos = new FileOutputStream(uploadPath+"\\"+fileName);
                /**
                 *绝对路径为F:\files
                 */
                FileOutputStream fos = null;
                //判断用户是否有F盘
                File file1 = new File("F:\\files");
                String f_name = fileName;
                //如果没有则在D盘创建

                fos = new FileOutputStream(f_name);
                IOUtils.copy(is, fos);
                //下载后增加用户积分
                user1.setTotal(user1.getTotal() + 10);
                //添加积分变动记录
                integralDao.addIntegral(new Integral(user1.getUid(), file.getId(), file.getF_title(), (+10), "上传资源赠送积分", ToolUtil.dateString(new Date())));
                userDao.updateUser(user1);
                fos.close();
                is.close();
                UploadImageUtils.uploadImageToServer(f_name, "192.168.17.12", "/www/server/nginx/files/", "root", "root");
            }
        }


        //获取当前时间
        String nowData = ToolUtil.dateString(new Date());
        //设置上传时间
        file.setF_uploadTime(nowData);
        System.out.println(file);

        //上传
        fileService.addFile(file);
        System.out.println("验证码正确");
        ToolSet.printWriter(resp, new Result(ResultStatus.UPLOAD_CODE_YES.getCode(), ResultStatus.UPLOAD_CODE_YES.getMsg()));


    }

    /**
     * 下载
     */
    public void downLoad(HttpServletRequest req, HttpServletResponse resp) throws IOException, ParseException, InterruptedException {
        //判断是不是会员用户
        boolean vip = Integer.parseInt(req.getParameter("vip")) == 1 ? true : false;
        Jedis jedis = new Jedis();
        //获取用户客户端标识
        String redisKey = Redis.getRedisKey(req);
        String s = jedis.get(redisKey);
        ObjectMapper objectMapper = new ObjectMapper();
        User user = objectMapper.readValue(s, User.class);
        /**
         * 暂时模拟一个user
         */
        System.out.println("开始下载.....");
        //获取文件存储路径
        ServletContext servletContext = this.getServletContext();
        //获取相对于tomcat的路径，存在不会实时更新的缺点
        //下载路径暂时设为上传的路径
        // String realPath =servletContext.getRealPath("files");
        File filePath = new File("F:\\files");
        if (!filePath.exists()) {
            //考虑到不同机器盘符不同问题
            filePath = new File("D:\\files");
        }
        //获取文件id
        Integer parameter = Integer.parseInt(req.getParameter("fid"));
        //根据文件id查询文件信息
        domian.File file = fileService.findFileById(parameter);
        //判断用户下载过该文件没 看是不是二次下载,三个月之内二次下载不扣积分
        boolean ver = verTime(2, parameter);
        //如果下载过 并且在三个月内
        if (!ver) {
            System.out.println("不是三个月内");
            boolean flag = verIntegral(user, file,req);
            //三个月内没下载过 并且积分不够
            if (!flag) {
                System.out.println("积分不够");
                return;
            }
        }
        //到此为
        //用户积分够下载文件
        //获取文件名称
        String fName = file.getF_name();
        //设置文件名称编码
        String fileEncodeName = URLEncoder.encode(fName, "utf-8");
        //总路径
        String fileName = filePath + "\\" + fName;
        //获取文件类型
        String mimeType = servletContext.getMimeType(fileName);
        //设置响应头类型
        resp.setHeader("content-type", mimeType);
        resp.setCharacterEncoding("utf-8");
        //设置内容打开方式为下载
        resp.setHeader("content-disposition", "attachment;filename=" + fileEncodeName);
        //获取输入缓冲流
        System.out.println("fileName:" + fileName);
        //从上传后的目录下载
        //从云盘下载
        fileName = "http://192.168.17.12:8060/" + fName;
//        System.out.println(fileName);
        //基于网络下载
        URL url = new URL(fileName);
        URLConnection urlConnection = url.openConnection();
        urlConnection.connect();


        //普通低速通道
        InputStream inputStream = urlConnection.getInputStream();
        ServletOutputStream outputStream = resp.getOutputStream();
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        Integer a;
        //vip高速通道
        if (vip) {
            System.out.println("vip");
            bis = new BufferedInputStream(inputStream);
            bos = new BufferedOutputStream(outputStream);
            byte[] bytes = new byte[10024];
            while ((a = bis.read(bytes)) != -1) {
                bos.write(bytes, 0, a);
            }
        } else {
            System.out.println("novip");
            //普通低速通道
            byte[] bytes = new byte[1];
            while ((a = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, a);
            }
        }


        //上传下载记录 暂时指定用户
        //DownInfo downInfo = new DownInfo(user.getUid(),parameter,ToolUtil.dateString(new Date()));
        DownInfo downInfo = new DownInfo(user.getUid(), parameter, ToolUtil.dateString(new Date()));
        downInfoService.addDownInfo(downInfo);
        //下载文件后通知文件作者
        InformMessage informMessage = new InformMessage(file.getF_userId(), "【下载】" + user.getNickname() + "下载了您的文件(" + file.getF_name() + ")", ToolUtil.dateString(new Date()));
        informMessageDao.addInform(informMessage);
        //释放资源
        if (bis != null) {
            bis.close();
        }
        if (bos != null) {
            bis.close();
        }
        outputStream.close();
        inputStream.close();
    }

    /**
     * 判断三个月内是否下载过文件
     *
     * @return
     */
    public boolean verTime(Integer uid, Integer fid) throws ParseException {
        //用户id暂时设置为1
        String upDownDataByUidAndFid = downInfoService.findUpDownDataByUidAndFid(3, fid);
        //如果没有下载过直接返回false
        if (upDownDataByUidAndFid == null) return false;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date parse = sdf.parse(upDownDataByUidAndFid);
        System.out.println(parse);
        //上次下载时间
        long lastTime = parse.getTime();
        //加上三个月后的时间
        long time = lastTime + (90 * 24 * 60 * 60 * 1000L);
        //与当前时间比较
        long nowTime = new Date().getTime();
        System.out.println("加三个月后:" + time);
        System.out.println("当前:" + nowTime);
        return nowTime < time;
    }

    /**
     * 判断用户积分是否足够下载文件，如果够扣除积分
     * 不够则返回false
     */
    public boolean verIntegral(User user, domian.File file,HttpServletRequest req) throws JsonProcessingException {
        if (user.getTotal() > file.getF_integral()) {
            System.out.println("积分足够");
            //减去积分
            user.setTotal(user.getTotal() - file.getF_integral());
            //更新用户积分数据
            userDao.updateUser(user);
            us.userUpdate(user,Redis.getRedisKey(req));
            //添加积分变动记录
            Integral integral = new Integral(user.getUid(), file.getId(), file.getF_title(), -file.getF_integral(), ResultStatus.INIEGRAL_CHANGE2.getMsg(), ToolUtil.dateString(new Date()));
            integralDao.addIntegral(integral);
            //文件下载次数+1
            file.setF_total(file.getF_total() + 1);
            fileService.updateFile(file);
            return true;
        }
        return false;
    }

    /**
     * 判断用户是否是会员
     */
    public void verVip(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        //暂时先模拟一个user
        Jedis jedis = new Jedis();
        //获取用户客户端标识
        String redisKey = Redis.getRedisKey(req);
        String s = jedis.get(redisKey);
        ObjectMapper objectMapper = new ObjectMapper();
        User user = objectMapper.readValue(s, User.class);
        Vip inquire = vipDao.inquire(user.getUid());
        if (inquire == null || inquire.getVipStatus() == 0) {
            Result result = new Result(ResultStatus.NO_VIP.getCode(), ResultStatus.NO_VIP.getMsg());
            System.out.println("不是会员用户！");
            ToolSet.printWriter(resp, result);
            return;
        }
        Result result = new Result(ResultStatus.YES_VIP.getCode(), ResultStatus.YES_VIP.getMsg());
        System.out.println("是会员用户！");
        ToolSet.printWriter(resp, result);


    }


}
